package com.haredot.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haredot.constraint.RedisConst;
import com.haredot.constraint.SolrConst;
import com.haredot.dto.ResourceSolrDTO;
import com.haredot.entity.*;
import com.haredot.event.RssNotifyEvent;
import com.haredot.event.enums.NotifyType;
import com.haredot.holder.TokenContextHolder;
import com.haredot.mapper.*;
import com.haredot.service.ResourceService;
import com.haredot.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.HighlightOptions;
import org.springframework.data.solr.core.query.SimpleHighlightQuery;
import org.springframework.data.solr.core.query.SimpleStringCriteria;
import org.springframework.data.solr.core.query.result.HighlightPage;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author Administrator
 * @description 针对表【tb_resource】的数据库操作Service实现
 * @createDate 2023-09-02 22:00:46
 */
@Service
public class ResourceServiceImpl extends ServiceImpl<ResourceMapper, Resource> implements ResourceService {

    @javax.annotation.Resource
    private ResourceTypeMapper resourceTypeMapper;

    @javax.annotation.Resource
    private RedisTemplate<String, ResourceViewVo> redisTemplate;

    @javax.annotation.Resource
    private CommentsMapper commentsMapper;

    @javax.annotation.Resource
    private SolrTemplate solrTemplate;

    @javax.annotation.Resource
    private CollectMapper collectMapper;

    @javax.annotation.Resource
    private UserScoreMapper userScoreMapper;

    @javax.annotation.Resource
    private ApplicationEventPublisher applicationEventPublisher ;

    @Override
    public R<ResourceType> queryResourceTypeLists() {

        List<ResourceType> resourceTypes = resourceTypeMapper
                .selectList(new LambdaQueryWrapper<ResourceType>().eq(ResourceType::getState, true));

        return R.<ResourceType>ok().data(resourceTypes);
    }

    @Override
    public R<?> saveResource(Resource resource) {
        // 添加资源
        return this.getBaseMapper().insert(resource) == 1 ? R.ok().build() : R.fail().build();
    }

    @Override
    public R<ResourceDetailVo> queryDetailsData(Long resId) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        Long userId = null;
        if (authentication != null && !"anonymousUser".equals(authentication.getName())) {
            // 查询用户信息
            userId = TokenContextHolder.getCurrentUserId();
        }

        ResourceDetailVo resourceDetailVo = this.getBaseMapper().queryResourceDetail(resId, userId);

        if (resourceDetailVo == null) return R.<ResourceDetailVo>fail("资源信息不存在").build();

        // 构建一个 ResourceViewVo 对象，并将对应的属性拷贝到 该对象中
        ResourceViewVo resourceViewVo = new ResourceViewVo();
        BeanUtils.copyProperties(resourceDetailVo, resourceViewVo);

        // 更新 redis 有序集合中 该资源的 访问量 ，自增 + 1
        redisTemplate.opsForZSet().incrementScore(RedisConst.RESOURCE_VISIT_RANK_SET, resourceViewVo, 1);
        // 查询 资源详情 ， 用户信息 , 收藏状态
        return R.<ResourceDetailVo>ok().data(resourceDetailVo);
    }

    @Override
    public R<Page<? extends Comments>> resourceComments(Page<? extends Comments> page, Long resId) {
        // 获取当前用户ID, 可能为 null
        Long currentUserId = TokenContextHolder.getCurrentUserId();
        Page<CommentsVo> data = commentsMapper.queryComments(page, currentUserId, resId);
        return R.<Page<? extends Comments>>ok().data(data);
    }

    @Override
    public HighlightPage<ResourceSolrDTO> fullSearchResource(Integer current, Integer rows, String word) {

        // 设置查询 条件
        Criteria criteria = new SimpleStringCriteria("text:" + word);
        // 设置分页信息
        PageRequest pageable = PageRequest.of(current, rows);
        // 设置高亮信息
        SimpleHighlightQuery simpleHighlightQuery = new SimpleHighlightQuery(criteria, pageable);

        HighlightOptions highlightOptions = new HighlightOptions();
        highlightOptions.addField("resourceName"); // 设置要高亮的域
        highlightOptions.setSimplePrefix("<em style='color:#f00'>");
        highlightOptions.setSimplePostfix("</em>");
        simpleHighlightQuery.setHighlightOptions(highlightOptions);

        // 准备 进行查询数据
        HighlightPage<ResourceSolrDTO> objects = solrTemplate.queryForHighlightPage(SolrConst.RESOURCE_SOLR_CORE, simpleHighlightQuery, ResourceSolrDTO.class);

        return objects;
    }

    @Override
    public R<Map<String, Object>> queryResourcesByPage(Page<Page<Map<String, Object>>> page, Long typeId) {
        // 根据 资源 类型 ID , 查询所有满足条件的资源信息
        Page<ResourceViewVo> pageResult = this.getBaseMapper().findResourcesByTypeIdAndPage(page, typeId);
        // 获取 该资源对应的 访问量
        List<Map<String, Object>> list = pageResult.getRecords().stream().map(resourceViewVo -> {
            Map<String, Object> map = new HashMap<>();
            map.put("value", resourceViewVo);
            map.put("score", redisTemplate.opsForZSet().score(RedisConst.RESOURCE_VISIT_RANK_SET, resourceViewVo));
            return map;
        }).toList();

        return R.<Map<String, Object>>ok().data(Map.of("list", list, "total", pageResult.getTotal()));
    }

    @Override
    public R<?> collectResource(Long resId) {
        Long userId = TokenContextHolder.getCurrentUserId();
        // 查询当前资源的上传者ID
        Resource resource = this.getBaseMapper().selectById(resId);
        // 判断 资源的上传者是否当前用户，自己无法收藏自己的资源
        if (Objects.equals(userId, resource.getUserId())) {
            return R.fail("您无法收藏自己上传的资源").build();
        }
        // 根据资源ID 、查询 该资源是否已被用户收藏
        Collect collect = collectMapper.selectOne(new LambdaQueryWrapper<Collect>()
                .eq(Collect::getUserId, userId)
                .eq(Collect::getResId, resId));

        if (collect != null) {
            // 删除该收藏
            collectMapper.deleteById(collect);
        } else {
            // 添加收藏
            collectMapper.insert(Collect.builder().userId(userId).resId(resId).build());

            String tel = SecurityContextHolder.getContext().getAuthentication().getName();
            String telSecure = tel.substring(0, 3) + "****" + tel.substring(7);
            // 发送通知给资源上传者
            Notify notify = Notify.builder()
                    .typeName(NotifyType.COLLECT.getTypeName())
                    .userId(resource.getUserId())
                    .resId(resId)
                    .text(MessageFormat.format("您上传的资源《{0}》被用户{1}收藏", resource.getResourceName(), telSecure))
                    .build();

            // 发送通知
            applicationEventPublisher.publishEvent(new RssNotifyEvent(this, notify));

        }
        return R.ok().build();
    }

    @Override
    public R<Map<String, Object>> queryMoreResourceByUid(Page<Page<ResourceVo>> page, Long uid) {

        Map<String, Object> map = new HashMap<>();

        Long total = this.getBaseMapper().findMoreResourceByUidCount(uid);

        map.put("total", total);

        Long current = page.getCurrent() , rows = page.getSize() ;
        Long offset = (current - 1) * rows ;
        if (offset < total) {
            // 调用存储过程、获取数据
            List<ResourceVo> resourceVoList = this.getBaseMapper().findMoreResourceByUid(offset, rows, uid);
            map.put("records", resourceVoList);
        }else{
            map.put("records", new ArrayList<>());
        }

        return R.<Map<String, Object>>ok().data(map);
    }

    @Override
    public R<Map<String, Integer>> checkDownLoadPermission(Long resId) {
        // s 代表积分数量、 r 是否记录下载次数， d 是否扣除积分
        // 获取当前登录的用户
        Long userId = TokenContextHolder.getCurrentUserId() ;
        // 根据 资源 ID , 查询该资源额基本信息
        Resource resource = this.getBaseMapper().selectById(resId);
        // 如果资源是当前用户上传的 或者当前用户是 svip ，允许直接下载资源、不扣除积分、不记录下载次数
        if (Objects.equals(userId, resource.getUserId()) || redisTemplate.hasKey("svip:" + userId)) {
            return R.<Map<String, Integer>>ok()
                    .data(Map.of("s", resource.getScore(), "r", 0, "d", 0)) ;
        }
        // 查询近 30 天 当前用户对该资源的 下载记录
        long count = this.getBaseMapper().findDownloadHistoryCountLast30Days(userId, resId);
        // 如果当前用户是普通用户、则判断该用户是否在 近 30 天内
        if (count > 0) {
            ResourceDownloadHistory history = ResourceDownloadHistory.builder()
                    .userId(userId)
                    .resId(resId)
                    .createTime(LocalDateTime.now())
                    .build();
            this.getBaseMapper().saveDownloadHistory(history);
            return R.<Map<String, Integer>>ok("近30天内下载过该资源、不扣除积分")
                    .data(Map.of("s", resource.getScore(), "r", 1, "d", 0)) ;
        }
        // 如果该资源积分为 0 ，则 允许下载，且不扣除积分
        if (resource.getScore() == 0) {
            ResourceDownloadHistory history = ResourceDownloadHistory.builder()
                    .userId(userId)
                    .resId(resId)
                    .createTime(LocalDateTime.now())
                    .build();
            this.getBaseMapper().saveDownloadHistory(history);

            return R.<Map<String, Integer>>ok().data(Map.of("s", 0, "r", 1, "d", 0)) ;
        }
        // 查询当前用户积分数量
        long totalScore = this.userScoreMapper.findUserTotalScore(userId) ;
        // 判断 积分是否满足下载需求
        if (totalScore < resource.getScore()) {
            return R.<Map<String, Integer>>fail("下载失败、积分不足").build();
        }
        UserScore userScore = UserScore.builder()
                .action("下载资源")
                .userId(userId)
                .resId(resId)
                .score(resource.getScore() * -1)
                .build();
        // 如果积分足够、扣除自身积分
        this.userScoreMapper.insert(userScore);

        UserScore byUserScore = UserScore.builder()
                .action("资源下载")
                .userId(resource.getUserId())
                .resId(resId)
                .score(resource.getScore())
                .build();
        //赠送上传者对应积分
        this.userScoreMapper.insert(byUserScore);

        // 记录下载信息
        ResourceDownloadHistory history = ResourceDownloadHistory.builder()
                .userId(userId)
                .resId(resId)
                .createTime(LocalDateTime.now())
                .build();
        this.getBaseMapper().saveDownloadHistory(history);

        String tel = SecurityContextHolder.getContext().getAuthentication().getName();
        String telSecure = tel.substring(0, 3) + "****" + tel.substring(7);
        // 发送通知给上传者
        Notify notify = Notify.builder()
                .userId(resource.getUserId())
                .resId(resId)
                .typeName(NotifyType.DOWN.getTypeName())
                .text(MessageFormat.format("您上传的资源《{0}》被用户{1}下载", resource.getResourceName(), telSecure))
                .build();

        applicationEventPublisher.publishEvent(new RssNotifyEvent(this, notify));

        return R.<Map<String, Integer>>ok()
                .data(Map.of("s", resource.getScore(), "r", 1, "d", 1));
    }
}




